package com.jzo2o.orders.manager.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.msg.TradeStatusMsg;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusChangeEventEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.client.AddressBookClient;
import com.jzo2o.orders.manager.service.client.MarketClient;
import com.jzo2o.orders.manager.service.client.ServeClient;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {
    @Resource
    private AddressBookClient addressBookClient;
    @Resource
    private ServeClient serveClient;
    @Resource
    private RedisTemplate<String, Long> redisTemplate;
    @Resource
    private IOrdersCreateService currentProxy;
    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private OrderStateMachine orderStateMachine;
    @Resource
    private MarketClient marketClient;
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) {
        //1. 获取服务地址信息：需要远程调用customer服务，根据dto里的addressBookId获取到
        AddressBookResDTO addressBook = addressBookClient.findById(dto.getAddressBookId());
        if (addressBook == null) {
            throw new ForbiddenOperationException("地址信息异常");
        }

        //2. 获取购买的服务信息：需要远程调用foundations服务，根据dto里的serveId获取到
        ServeAggregationResDTO serve = serveClient.findById(dto.getServeId());
        if (serve == null) {
            throw new ForbiddenOperationException("服务信息异常");
        }

        //3. 封装Orders对象
        Orders orders = new Orders();
        //  订单id：yyMMdd + 13位序列号。 序列号使用redis的incr命令自增实现
        orders.setId(generateOrderId());
        orders.setUserId(UserContext.currentUserId());
        orders.setServeTypeId(serve.getServeTypeId());
        orders.setServeTypeName(serve.getServeTypeName());
        orders.setServeItemId(serve.getServeItemId());
        orders.setServeItemName(serve.getServeItemName());
        orders.setServeItemImg(serve.getServeItemImg());
        orders.setUnit(serve.getUnit());
        orders.setServeId(serve.getId());
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        orders.setPrice(serve.getPrice());
        orders.setPurNum(NumberUtils.null2Default(dto.getPurNum(), 1));
        //  总金额：单价*数量
        orders.setTotalAmount(orders.getPrice().multiply(new BigDecimal(orders.getPurNum())));
        //  优惠金额：还没有实现优惠券功能，先暂时写死成0
        orders.setDiscountAmount(BigDecimal.ZERO);
        //  实际支付金额：总金额 - 优惠金额
        orders.setRealPayAmount(NumberUtils.sub(orders.getTotalAmount(), orders.getDiscountAmount()));
        orders.setCityCode(serve.getCityCode());
        orders.setServeAddress(addressBook.getProvince() + addressBook.getCity() + addressBook.getCounty() + addressBook.getAddress());
        orders.setContactsPhone(addressBook.getPhone());
        orders.setContactsName(addressBook.getName());
        orders.setServeStartTime(dto.getServeStartTime());
        orders.setLon(addressBook.getLon());
        orders.setLat(addressBook.getLat());
        //  排序字段：服务开始时间秒级时间戳 + 订单id后6位
        orders.setSortBy(DateUtils.toEpochMilli(dto.getServeStartTime()) / 1000 + orders.getId() % 1000000);
        //4. 保存到orders表：如果通过this.add(orders),直接调用当前对象(OrdersCreateServiceImpl类对象)的add方法，没有走代理
        //currentProxy.add(orders);

        //4. 保存到orders表：
        if (ObjectUtils.isNull(dto.getCouponId())) {
            //下单时没有选择优惠券，直接保存订单
            currentProxy.add(orders);
        }else{
            //下单时选择了优惠券，核销优惠券并计算优惠金额，然后再保存订单
            currentProxy.addWithCoupon(orders, dto.getCouponId());
        }

        //5. 返回结果
        PlaceOrderResDTO res = new PlaceOrderResDTO();
        res.setId(orders.getId());
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(Orders orders) {
        boolean saved = save(orders);
        if (!saved) {
            throw new DBException("保存订单失败");
        }
        //通过状态机启动状态管理
        orders = getById(orders.getId());
        OrderSnapshotDTO snapshot = BeanUtils.toBean(orders, OrderSnapshotDTO.class);
        orderStateMachine.start(orders.getUserId(), orders.getId().toString(), snapshot);
    }

    @Override
    public OrdersPayResDTO pay(Long orderId, OrdersPayReqDTO dto) {
        //1. 查询订单
        Orders order = getById(orderId);
        if (order == null) {
            throw new BadRequestException("订单不存在");
        }
        //2. 判断订单支付状态
        if (order.getPayStatus() == OrderPayStatusEnum.PAY_SUCCESS.getStatus()
                && order.getTradingOrderNo() != null) {
            //直接返回结果
            OrdersPayResDTO res = new OrdersPayResDTO();
            res.setProductOrderNo(orderId);
            res.setTradingOrderNo(order.getTradingOrderNo());
            res.setTradingChannel(order.getTradingChannel());
            res.setPayStatus(order.getPayStatus());
            return res;
        }
        //3. 远程调用trade支付服务申请支付，获取二维码
        NativePayReqDTO nativePayReq = new NativePayReqDTO();
        //  是否切换了支付渠道：如果提交的参数渠道  和 order里上次的渠道不同，就说明要切换支付渠道了
        nativePayReq.setChangeChannel(ObjectUtils.notEqual(dto.getTradingChannel().getValue(), order.getTradingChannel()));
        nativePayReq.setTradingChannel(dto.getTradingChannel());
        nativePayReq.setMemo("预约服务");
        nativePayReq.setProductOrderNo(orderId);
        if (dto.getTradingChannel() == PayChannelEnum.WECHAT_PAY) {
            nativePayReq.setEnterpriseId(tradeProperties.getWechatEnterpriseId());
        } else if (dto.getTradingChannel() == PayChannelEnum.ALI_PAY) {
            nativePayReq.setEnterpriseId(tradeProperties.getAliEnterpriseId());
        }
        //  设置productAppId:我们的预约订单，使用一个固定值jzo2o.orders
        nativePayReq.setProductAppId("jzo2o.orders");
        nativePayReq.setTradingAmount(order.getRealPayAmount());
        //远程调用申请支付，得到结果
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReq);
        if (downLineTrading == null) {
            throw new CommonException("订单" + orderId + "申请支付失败");
        }
        //把结果里支付渠道和交易单号更新到orders表里
        boolean updated = lambdaUpdate()
                .set(Orders::getTradingChannel, downLineTrading.getTradingChannel())
                .set(Orders::getTradingOrderNo, downLineTrading.getTradingOrderNo())
                .eq(Orders::getId, orderId)
                .update();
        if (!updated) {
            throw new CommonException("订单" + orderId + "更新交易单号失败");
        }
        //构造返回值
        OrdersPayResDTO res = new OrdersPayResDTO();
        res.setProductOrderNo(orderId);
        res.setTradingOrderNo(downLineTrading.getTradingOrderNo());
        res.setTradingChannel(downLineTrading.getTradingChannel());
        res.setQrCode(downLineTrading.getQrCode());
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(TradeStatusMsg tradeStatusMsg) {
        /*boolean updated = lambdaUpdate()
                .set(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                .set(Orders::getPayTime, LocalDateTime.now())
                .set(Orders::getTradingOrderNo, tradeStatusMsg.getTradingOrderNo())
                .set(Orders::getTransactionId, tradeStatusMsg.getTransactionId())
                .eq(Orders::getId, tradeStatusMsg.getProductOrderNo())
                .update();
        if (!updated) {
            throw new DBException("更新订单" + tradeStatusMsg.getProductOrderNo() + "支付状态异常");
        }*/
        //支付成功后，期望让状态机也存储支付相关的一些状态
        OrderSnapshotDTO snapshot = new OrderSnapshotDTO();
        snapshot.setTradingChannel(tradeStatusMsg.getTradingChannel());
        snapshot.setThirdOrderId(tradeStatusMsg.getTransactionId());
        snapshot.setTradingOrderNo(tradeStatusMsg.getTradingOrderNo());
        snapshot.setPayTime(LocalDateTime.now());
        snapshot.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
        Orders orders = getById(tradeStatusMsg.getProductOrderNo());
        orderStateMachine.changeStatus(orders.getUserId(), tradeStatusMsg.getProductOrderNo().toString(), OrderStatusChangeEventEnum.PAYED, snapshot);
    }

    @Override
    public OrdersPayResDTO payResult(Long orderId) {
        //1. 查订单
        Orders order = getById(orderId);
        if (order == null) {
            throw new BadRequestException("订单不存在");
        }
        //2. 如果订单状态不是已支付，就需要远程调用trade服务，查询一下最新的支付状态。如果是已支付，要更新订单状态
        if (ObjectUtils.equal(order.getOrdersStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            //远程调用trade服务查询最新支付状态
            TradingResDTO tradeResult = tradingApi.findTradResultByTradingOrderNo(order.getTradingOrderNo());
            if (ObjectUtils.equal(tradeResult.getTradingState(), TradingStateEnum.YJS)) {
                //如果是已支付，需要更新订单状态
                TradeStatusMsg tradeStatusMsg = new TradeStatusMsg();
                tradeStatusMsg.setTradingOrderNo(tradeResult.getTradingOrderNo());
                tradeStatusMsg.setTransactionId(tradeResult.getTransactionId());
                tradeStatusMsg.setProductOrderNo(orderId);
                currentProxy.paySuccess(tradeStatusMsg);

                //构造返回值
                OrdersPayResDTO res = new OrdersPayResDTO();
                res.setTradingChannel(tradeResult.getTradingChannel());
                res.setTradingOrderNo(tradeResult.getTradingOrderNo());
                res.setProductOrderNo(orderId);
                res.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
                return res;
            }
        }

        //3. 返回结果
        OrdersPayResDTO res = new OrdersPayResDTO();
        res.setTradingChannel(order.getTradingChannel());
        res.setTradingOrderNo(order.getTradingOrderNo());
        res.setProductOrderNo(orderId);
        res.setPayStatus(order.getPayStatus());
        return res;
    }

//获取可用优惠卷列表
    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupons(Long serveId, Integer purNum) {
        //1. 获取服务信息
        ServeAggregationResDTO serve = serveClient.findById(serveId);
        if (serve == null || ObjectUtils.notEqual(serve.getSaleStatus(), 2)) {
            throw new BadRequestException("服务不可用");
        }

        //2. 计算总金额
        BigDecimal totalAmount = serve.getPrice().multiply(new BigDecimal(purNum));

        //3. 获取可用的优惠券列表
        return marketClient.getAvailableCoupons(totalAmount);
    }

    @GlobalTransactional
    @Override
    public void addWithCoupon(Orders orders, Long couponId) {
        //远程调用核销优惠券，获取优惠金额
        CouponUseReqDTO dto = new CouponUseReqDTO();
        dto.setId(couponId);
        dto.setOrdersId(orders.getId());
        dto.setTotalAmount(orders.getTotalAmount());
        CouponUseResDTO res = marketClient.useCoupon(dto);
        //修改订单中的优惠金额
        orders.setDiscountAmount(res.getDiscountAmount());
        //修改订单的实付金额
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));
        //保存订单
        add(orders);
    }

    public Long generateOrderId() {
        //获取自增序列号
        Long number = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR);
        //yyMMdd左移13个数字 + 序列号。     20241220*10000000000000  + 1
        return DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + number;
    }

}
